

/*
 * @Author: lsmi
 * @Date: 2021-08-28 15:25:58
 * @LastEditors: lsmi
 * @LastEditTime: 2021-11-03 12:59:57
 * @FilePath: \base-typescript\src\index.ts
 */
let a: {
  b: number
  c?: string
  [key: number]: boolean
  [key: string]: unknown
  readonly name: string
}
a = {
  b: 5,
  c: undefined,
  ip: 0,
  name: 'xx'
}

type Color = 'red'

const ac = Math.random() < 0.5
if (ac) {
  type Color = 'blue'
  const t: Color = 'blue'
}
const t: Color = 'red'

type Returns = string | null

function buildArray() {
  const a = []
  a.push(1)
  a.push('true')
  return a
}
const myArray = buildArray()
myArray.push('true')

// let trainFares: [number, string?][] = [
//   [20],
//   [10]
// ]
let trainFares: ([number] | [number, string?])[] = [
  [20],
  [10]
]
// 至少一个string 
let friends: [string, ...number[]] = ['sss', 20]

let list: [string, boolean, ...number[]] = ['string', false, 50]

let as: readonly number[] = [2]
// as[2] = 52
// as.push(2)
let bs: readonly number[] = as.concat(5)
function aaaa() {
  type A = readonly string[]
  type B = ReadonlyArray<string>
  type C = Readonly<string[]>

  type D = readonly [string, number]
  type E = Readonly<[string, number]>
  let as: readonly number[] = [20, 50]
  let bs: readonly number[] = as.concat([60])
}
function b() {
  // 默认是0 1 2
  enum Language {
    English = 2,
    Spanish = 3,
    Russian = 4
  }
  enum Language {
    Test = 5 + 20,
    IsAddCount // 自动推到出的值是 26 （25 + 1）
  }
  let myFirstLanguage = Language.Russian
  let mySecondLanguage = Language['Russian']
  let myThirdLanguage = Language[4]
  let myTestLanguage = Language.Test

  enum Color {
    Red = '#c10000',
    Blue = '#007ac1',
    Pink = 0xc10050,
    White = 255
  }
  let red = Color.Red
  let pink = Color.Pink
  let red2 = Color['Red']
  let red3 = Color[0] // string
  let d = Color[20] // 20没有这个索引 但还是 string

  // 为了避免这种不安全的访问操作，可以通过const enum指定使用枚举的安全子集
  const enum Languages {
    English,
    Spanish,
    Russian
  }
  let a = Languages.English //Language
  // let b = Languages.Tagalog
  // let c = Languages[0] // 报错
  // let dd = Languages[6]
  // 如果想为const enum 生成运行时代码，在tsconfig.json中把TSC选项preserveConstEnum设为true
  // {
  //   "complierOptions": {
  //     "preserveConstEnum": true
  //   }
  // }

  // const enum Flippable{
  //   Burger,
  //   Chair,
  //   Cup,
  //   Table,
  //   Data
  // }
  const enum Flippable {
    Burger = 'Burger',
    Chair = 'Chair',
    Cup = 'Cup',
    Table = 'Table',
    Data = 'Data'
  }
  function flip(f: Flippable) {
    return 'flipped it'
  }
  flip(Flippable.Burger) // "flipped it"
  flip(Flippable.Chair) // "flipped it"
  // flip(12) // "flipped it"  (!!!!)  为解决这个问题 修改全部键值 Burger = 'Burger'
  // flip('Hat')
}
function c() {
  let greet = function (name: string) {
    return 'hello ' + name
  }
  let greet2 = new Function('name', 'return "hello" + name')
  function greet3(name: string) {
    return 'hello' + name
  }
  let greet4 = (name: string) => {
    return 'hello ' + name
  }
  let greet5 = (name: string) => 'hello ' + name

  type Context = {
    userId?: string
    appId?: string
  }
  function log(message: string, context: Context = {}) {
    let time = new Date().toISOString()
    console.log(time, message, context.userId)
  }
  function sumVar(): number {
    return Array.from(arguments).reduce((total, i) => total + i, 0)
  }
  // sumVar(1,2,3)
  function sumVarSafe(...number: number[]) {
    return number.reduce((total, i) => total * i, 0)
  }
  sumVarSafe(1, 2)

  function add(a: number, b: number) {
    return a + b
  }
  add(1, 2)
  add.bind(null, 1, 2, 5)()
  add.apply(null, [1, 2])
  add.call(null, 1, 3)
  // 为了安全使用.bind .apply .call 要在tsconfig.json 中启用strictBindCallApply选项（启用strict模式后自动启用该选项）
}
function d() {
  function fancyDate(this: Date): string {
    return `${this.getDate()}/${this.getMonth()}/${this.getFullYear()}`
  }
  fancyDate.apply(new Date)
  // 如果想强制显式注解函数中this的类型，在tsconfig.json中启用noImplicitThis设置。strict模式包括noImplicitThis，如果
  // 已经启用该模式，就不用设置noImplicitThis，注意，noImplicitThis不强制要求为类或对象的函数注解this
}
function e() {
  // 有*表示生成器
  function* createFibonacciGenerator() {
    let a = 0
    let b = 1
    while (true) {
      yield a;
      [a, b] = [b, a + b]
    }
  }
  let filbGenerator = createFibonacciGenerator()
  filbGenerator.next()

  function* createNumbers(): IterableIterator<number> {
    let n = 0
    while (1) {
      yield n++
    }
  }
  let numbers = createNumbers()

  let objNumbers = {
    *[Symbol.iterator]() {
      for (let i = 1; i <= 10; i++) {
        yield i
      }
    }
  }

  // 两种等效
  type Log = (message: string) => void
  type Logs = {
    (message: string): void
  }

  type Reserve = {
    (form: Date, to: Date, desc: string): void
    (form: Date, desc: string): void
  }
  // type Reserve = (form: Date, to: Date, desc: string) => void
  // 由于第二个参数可能是to也可能是desc 所以中间类型是联合的 第三个是可选的
  const reserve: Reserve = (form: Date, toOrDesc: Date | string, desc?: string) => {
    return form + (desc || '') + toOrDesc
  }
  // function reserve(form, to, desc): Reserve{ // 报错
  //   return form + to + desc
  // }

  type WarnUser = {
    (message: string): void
    loading: boolean
  }
  const warnUser: WarnUser = (message) => {
    if (warnUser.loading) {
      return
    }
    warnUser.loading = true
    console.log(message)
  }
  warnUser.loading = false
}
function f() {
  type MyFilter = {
    // (array: number[], f: (t: number) => boolean): number[]
    // (array: string[], f: (t: string) => boolean): string[]
    (array: object[], f: (t: object) => boolean): object[]
  }
  const myFilter: MyFilter = (array, f) => {
    let result = []
    for (let i = 0; i < array.length; i++) {
      const item = array[i]
      if (f(item)) {
        result.push(item)
      }
    }
    return result
  }
  let names = [
    { name: 'beth' },
    { name: 'cait' },
    { name: 'flase' }
  ]
  // let result = myFilter(names, (item) => item.name.startsWith('b')) 
  //  报错  item是object类型  但不知道object下有name属性 所以ts报错  改为泛型
  // 局部签名
  type MyFilter2 = {
    <T>(array: T[], f: (t: T) => boolean): T[]
  }
  // ts自动推导类型
  const myFilter2: MyFilter2 = (array, f) => {
    let result = []
    for (let i = 0; i < array.length; i++) {
      const item = array[i]
      if (f(item)) {
        result.push(item)
      }
    }
    return result
  }
  myFilter2(names, (item) => item.name.startsWith('b'))

  // 显式绑定类型  全局签名
  type MyFilter3<T> = {
    (array: T[], f: (t: T) => boolean): T[]
  }
  const myFilter3: MyFilter3<number> = (array, f) => {
    let result = []
    for (let i = 0; i < array.length; i++) {
      const item = array[i]
      if (f(item)) {
        result.push(item)
      }
    }
    return result
  }
  type Filter1 = <T>(array: T[], f: (t: T) => boolean) => T[]
  type Filter2<T> = (array: T[], f: (t: T) => boolean) => T[]
  function filter10<T>(array: T[], f: (t: T) => boolean) { }
  filter10(names, t => t.name.startsWith('b'))
}
function g() {
  function myMap<T, U>(array: T[], f: (t: T) => U): U[] {
    let res = []
    for (let i = 0; i < array.length; i++) {
      const item = array[i]
      res[i] = f(item)
    }
    return res
  }
  // 这里ts会自己推导类型
  myMap(['a', 'b', 'c'], _ => _ === 'a')
  // 显式注解 不能只写一个 要么全写，要么不写
  myMap<string, boolean>(['a', 'b', 'c'], _ => _ === 'a')
  // 不会报错 因为 boolean 可赋值给 boolean | number
  myMap<string, boolean | number>(['a', 'b', 'c'], _ => _ === 'a')

  let promise = new Promise<number>((resolve) => {
    resolve(50)
  })
  promise.then(res => {
    console.log(res.toFixed(2))
  })
  // 泛型别名 type A<T> = { target: T } 在=之前  function cd<T>()

  //////
  // 使用二叉树示例
  type TrueNode = { value: string }
  // TrueNode的子类型
  type LeafNode = TrueNode & { isLeaf: true }
  type InnerNode = TrueNode & { child: [TrueNode] | [TrueNode, TrueNode] }
  let a: TrueNode = { value: 'a' }
  let b: LeafNode = { value: 'b', isLeaf: true }
  let c: InnerNode = { value: 'c', child: [b] }
  // T的类型可能是LeafNode | InnerNode | TrueNode  并且T是自动推导的
  function mapNode<T extends TrueNode>(node: T, f: (t: string) => string) {
    return { ...node, value: f(node.value) }
  }
  let a1 = mapNode(a, _ => _.toUpperCase()) // TrueNode
  let b1 = mapNode(b, _ => _.toUpperCase()) // LeafNode
  let c1 = mapNode(c, _ => _.toUpperCase()) // InnerNode
  let d1 = mapNode({ value: '' }, _ => _.toUpperCase())
  //////

  /////
  type HasSides = { numberOfSides: number }
  type SideHaveLength = { sideLength: number }
  // Shape extends (HasSides & SideHaveLength)  和  Shape extends HasSides & SideHaveLength 一样
  // &  是不能少一个的  不然报错
  function logPerimeter<Shape extends HasSides & SideHaveLength>(s: Shape): Shape {
    console.log(s.numberOfSides * s.sideLength)
    return s
  }
  type Square = HasSides & SideHaveLength
  let square: Square = { numberOfSides: 4, sideLength: 3 }
  logPerimeter(square) //
  ////


  // function call(
  //   f: (...args: unknown[]) => unknown,
  //   ...args: unknown[]
  // ): unknown{
  //   return f(...args)
  // }
  // T是任意类型的数组或元组
  function call<T extends unknown[], R>(
    f: (...args: T) => R,
    ...args: T
  ): R {
    return f(...args)
  }
  // call函数中的泛型T在第一个参数fill的传入时就已经确定了类型，所以call的第二个参数必须是length: number, value: string的组合。
  function fill(length: number, value: string): string[] {
    return Array.from({ length }, () => value)
  }
  let aa = call(fill, 10, 'a')
  // let bb = call(fill, 10)
  // let cc = call(fill, 10, 'a', 'z')
  // let dd = call(fill, 'st', 10)
  // type TestIs<T extends unknown> = {
  //   (...args: T[]): boolean
  // }
  // type Foo = 'foo'
  // type Nu = number

  function is<T>(a: T, ...b: [T, ...T[]]): boolean {
    return b.every(_ => _ === a)
  }
  // Compare a string and a string
  is('string', 'otherstring') // false

  // Compare a boolean and a boolean
  is(true, false) // false

  // Compare a number and a number
  is(42, 42) // true

  // Comparing two different types should give a compile-time error
  // is(10, 'foo')
  is([1], [1, '2'])
  console.log(is([1, { a: undefined }, [2, undefined]], [1, { a: null }, [2, null]]))
}
function h() {
  // 给泛型参数指定一个默认值
  type MyEvent<T = EvalError> = {
    target: T
    type: string
  }
  // type MyEvent<T extends EvalError = EvalError> = {
  //   target: T
  //   type: string
  // }
  // 默认参数也要放在最后
  // 错误
  // type MyEvent02<T extends EvalError = EvalError, U extends string> = {
  //   target: T
  //   type: U
  // }
  type MyEvent02<U extends string, T extends EvalError = EvalError> = {
    target: T
    type: U
  }
  // type Is = {
  //   (a: T, b: T): boolean
  // }
  // const is: Is = (a, b) => {
  //   return a === b
  //   // let res = true
  //   // let pre = null
  //   // for(let i = 0; i<args.length; i++){
  //   //   let judge = typeof args[i]
  //   //   if(judge === 'object'){
  //   //     // 对象
  //   //   }else{
  //   //     if(pre){
  //   //       const nextJudge = pre === args[i]
  //   //       !nextJudge && (res = false)
  //   //       return
  //   //     }else{
  //   //       pre = args[i]
  //   //     }
  //   //   }
  //   // }
  //   // return res
  // }
  // is(10, 20)
}
function i() {
  // super()  super.take调用父类的方法， super不能访问父类的属性
  // public 任何地方都能访问 protected 可由当前类及子类的实例访问 private 只由当前类实例访问
  // 表示一次游戏
  // class Game { }
  // 棋子
  // class Piece {}
  // 一个棋子的一组坐标
  // class Position {}

  // 有6类棋子
  // class King extends Piece{}
  // class Queen extends Piece{}
  // class Bishop extends Piece{}
  // class Knight extends Piece{}
  // class Rook extends Piece{}
  // class Pawn extends Piece{}
  class Queen { }
  class Game {
    private pieces = Game.makePieces()
    private static makePieces() {
      return [
        // 王
        new King('White', 'E', 1),
        new King("Block", "E", 8),
        // new Queen("")
      ]
    }
  }

  type Color = 'White' | 'Block'
  // x
  type File = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H'
  // y
  type Rank = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8

  class Position {
    constructor(private file: File, private rank: Rank) { }
    distanceFrom(position: Position) {
      return {
        rank: Math.abs(position.rank - this.rank),
        file: Math.abs(position.file.charCodeAt(0) - this.file.charCodeAt(0))
      }
    }
  }
  // 不希望用户直接实例化 Piece 使用abstract关键字 可以在类中定义方法
  abstract class Piece {
    protected position: Position
    constructor(private readonly color: Color, file: File, rank: Rank) {
      this.position = new Position(file, rank)
    }
    // Piece类为moveTo方法提供了默认实现（如果子类愿意，也可以覆盖默认实现）
    moveTo(position: Position) {
      this.position = position
    }
    // 告诉子类必须实现一个名为canMoveTo的方法 如果子类没有实现该方法就会报错
    abstract canMoveTo(position: Position): boolean
  }
  class King extends Piece {
    canMoveTo(position: Position) {
      let distance = this.position.distanceFrom(position)
      return distance.rank < 2 && distance.file < 2
    }
  }



  class Set {
    has(value: number): boolean {
      return false
    }
    // 
    // add(value: number): Set{

    // }
  }
  class MutableSet extends Set {
    delete(value: number): boolean {
      return false
    }
    // 覆盖签名方法比较麻烦  正确是使用this注解返回类型
    // add(value: number): MutableSet{

    // }
  }
  class Set1 {
    // add(value: number): this{

    // }
  }
}
function j() {
  type Food = {
    calories: number
    tasty: boolean
  }
  type Sushi = Food & { salty: boolean }
  type Cake = Food & { sweet: boolean }
  // 类型别名 和 接口
  interface Food1 {
    calories: number
    tasty: boolean
  }
  interface Sushi1 extends Food1 {
    salty: Boolean
  }
  interface Cake1 extends Food1 {
    sweet: boolean
  }

  interface A {
    good(x: number): string
    bad(x: number): string
  }
  // 这样会报错  用类型别名 extends 换 & ts就不会报错
  interface B extends A {
    good(x: string): string
    bad(x: string): string
  }
  type AA = {
    good(x: number): string
    bad(x: number): string
  }
  // 后面覆盖前面的
  type BB = AA & {
    good(x: string): string
    bad(x: string): string
  }
  // 在同一个域中的多个同名接口将自动合并，
  interface ABC {
    a(): boolean
  }
  interface ABC {
    b(): boolean
  }
  const aaa: ABC = {
    a: () => false,
    b: () => false
  }
  interface User {
    age: number
  }
  // 报错
  interface User {
    age: string
  }
  // 报错
  // interface User<Age extends number>{
  //   age: Age
  // }
  interface User1<Age extends string> {
    age: Age
  }
  // 不能像type自动推导
  const ap1: User1 = { age: 5 }
  const ap: User1<number> = { age: 5 }
  // 声明类时，可以使用implements关键字指明该类满足某个接口。与其他显式类型注解一样，这是为类添加类型层面约束的一种便利方式。
  // 这么做能尽量保证类在实现上的真确性，防止错误出现在下游，不知具体原因。这也是实现常用的设计模式（如适配器、工厂和策略）的
  // 一种常见方法
  interface Animal {
    eat(food: string): void
    sleep(hours: number): void
    // sleep?(hours: number): void
  }
  // 少一个属性方法就报错
  class Cat implements Animal {
    eat(food: string) {
      console.log('eat')
    }
    sleep(h: number) {
      console.log('sleep')
    }
    other(s: string) {

    }
  }
  interface Animal1 {
    readonly name: string
    eat(food: string): void
  }
  interface Feline {
    meow(): void
  }
  class Cat1 implements Animal1, Feline {
    name = 'sss'
    eat() { }
    meow() { }
  }
  class Zebra {
    trot() { }
  }
  class Poodle {
    trot() { }
  }
  function ambleAround(animal: Zebra) {
    animal.trot()
  }
  let zebra = new Zebra
  let poole = new Poodle
  // 两个都行  只要Poodle可赋值给Zebra，ts就不报错 因为在改函数看来 二者是可互用的 毕竟都实现了trot方法
  ambleAround(zebra)
  ambleAround(poole)
  ambleAround({ trot: () => { } })

  class AC {
    private x = 1
  }
  class AB extends AC { }
  function f(a: AC) { }
  f(new AC)
  f(new AB)
  // f({ x: 2 }) // 报错

}
function k() {
  type State = {
    [key: string]: string
  }
  class StringDatabase {
    state: State = {}
    constructor(state: State = {}) {

    }
    get(key: string): string | null {
      return key in this.state ? this.state[key] : null
    }
    set(key: string, value: string): void {
      this.state[key] = value
    }
    static from(state: State) {
      let db = new StringDatabase
      for (let key in state) {
        db.set(key, state[key])
      }
      return db
    }
  }
  interface StringDatabase {
    state: State
    get(key: string): string | null
    set(key: string, value: string): void
  }
  // 构造方法类型 typeof StringDatabase
  interface StringDatabaseConstructor {
    new(state?: State): StringDatabase // 构造方法签名  指定的类型可以使用new运算符实例化
    from(state: State): StringDatabase
  }
}
function l() {
  // 多态
  class MyMap<K, V>{
    // 不能在构造方法中声明泛型
    constructor(initKey: K, initValue: V) {

    }
    get(key: K) { }
    set(key: K, value: V) { }
    // merge<K1, V1>(map: MyMap<K1, V1>): MyMap<K|K1, V|V1>{}
    // 静态方法不能访问类的泛型， 这就像在值层面不能访问类的实例变量一样  of不能访问MyMap中声明的K和V， 可以声明自己的泛型 KK和VV
    // static of<KK,VV>(k: KK, v: VV): MyMap<K, V>{}
  }
  interface MyMap1<K, V> {
    get(key: K): V
    set(key: K, v: V): void
  }
  let a = new MyMap<string, number>('x', 4)
  let b = new MyMap(false, 4)
  // 混入
  // 构造方类型的参数的类型必须是any[]（不能是void、unknown[]）这样才能扩展
  type ClassConstructor<T> = new (...args: any[]) => T
  function withEZDebug<C extends ClassConstructor<{ getDebugValue(): object }>>(Cl: C) {
    // 返回一个匿名类构造方法
    return class extends Cl {
      // constructor(...args: any[]){
      //   super(...args)
      // }
      debug() {
        let name = Cl.constructor.name
        let value = this.getDebugValue()
        return `${name}(${JSON.stringify(value)})`
      }
    }
  }
  class HardToDebugger {
    constructor(
      private id: number,
      private firstName: string,
      private laseName: string
    ) { }
    getDebugValue() {
      return {
        id: this.id,
        name: this.firstName + this.laseName
      }
    }
    eat() {

    }
  }
  let User = withEZDebug(HardToDebugger)
  let user = new User(3, 'Ema', "Glu")
  user.debug() // 'User({ id: 3, name: '' })'
}
function m() {
  // 装饰器 是ts的一个实验特性，为类、类方法、属性和方法参数的远编程提供简洁的句法。其实，装饰器就是在装饰目标上调用函数的一种方法
  type ClassConstructor<T> = new (...args: any[]) => T
  function serial<T extends ClassConstructor<{ getValue(): string }>>(Con: T) {
    return class extends Con {
      constructor(...args: any[]) {
        super(...args)
      }
      serialize() {
        return this.getValue().toString()
      }
    }
  }
  // 这个装饰器不能调用 装饰器的方法 只能使用属性（非方法）
  @serial
  class APiLoad {
    getValue() {
      return 'get view'
    }
  }
  let yt = new APiLoad
  let DecPayload = serial(APiLoad)
  let payload = new DecPayload
  payload.serialize()

  class MessageQueue {
    private constructor(private message: string[]) {

    }
  }
  // 模拟final类
  // new MessageQueue() 报错constructor 设置为private 不能实例和继承 主要禁止扩展 要实例自己在类内部实现一个静态方法
  class MessageQueue1 {
    private constructor(private message: string[]) {

    }
    static create(message: string[]) {
      return new MessageQueue1(message)
    }
  }
  let messageQueue1 = MessageQueue1.create([])
  class MessageQueue2 {
    protected constructor(protected message: string[]) {

    }
    static create(message: string[]) {
      return new MessageQueue2(message)
    }
  }
  let messageQueue2 = MessageQueue2.create([])
  // 工厂模式
  type Shoe = {
    purpose: string
  }
  class BalletFlat implements Shoe {
    purpose = 'dancing'
  }
  class Boot implements Shoe {
    purpose = 'woodcutting'
  }
  class Sneaker implements Shoe {
    purpose = 'walking'
  }
  // 下面制造鞋工厂
  let Shoe = {
    create(type: 'balletFlat' | 'boot' | 'sneaker') {
      switch (type) {
        case 'balletFlat': return new BalletFlat
        case 'boot': return new Boot
        case 'sneaker': return new Sneaker
      }
    }
  }
  Shoe.create('boot')

  // Pick使用
  type Pick1<T, U extends keyof T> = {
    [key in U]: T[key]
  }
  interface TState {
    name: string
    age: number
    sex: boolean
  }
  interface TState1 extends Pick<TState, 'name' | 'age'> {
    gg: string
  }
  let yp: TState1 = {
    name: '',
    age: 1,
    gg: 'x'
  }
  type State = {
    name: string
    age: number
    sex: boolean
  }
}
function n() {
  const a = { x: 3 } as const
  type Options = {
    baseURL: string
    method: string
    params?: string
  }
  // 使用as Options ts将不管新的字段
  const as = {
    baseURL: 'xxxx',
    method: '',
    data: { x: 5 }
  } as Options
  class API {
    constructor(private options: Options) { }
  }
  let obj = {
    baseURL: 'xxx',
    method: 'xxx',
    more: true
  }
  // ts不做多余属性检查 所以不报错
  new API(obj)

  type Unit = "cm" | "px" | "rpx" | "%" | "rem"
  let units: Unit[] = ['cm', 'px']

  type UserTextEvent = { value: string }
  type USerMouseEvent = { value: [number, number] }
  type UserEvent = UserTextEvent | USerMouseEvent
  function handle(event: UserEvent) {
    if (typeof event.value === 'string') {
      event.value.indexOf('x') // string
    }
    event.value.slice() // [number, number]
  }
  type UserTextEvent1 = { value: string, target: [] }
  type USerMouseEvent1 = { value: [number, number], target: string }
  type UserEvent1 = UserTextEvent1 | USerMouseEvent1
  function handle1(event: UserEvent1) {
    if (typeof event.value === 'string') {
      event.target // [] | string
    }
    event.value // [number, number]
    event.target // [] | string
  }
  // 标记不要使用泛型 
  type UserTextEvent2 = { type: 'text', value: string, target: [] }
  type USerMouseEvent2 = { type: 'mouse', value: [number, number], target: string }
  type UserEvent2 = UserTextEvent2 | USerMouseEvent2
  function handle2(event: UserEvent2) {
    if (event.type === 'text') {
      event.target // []
    }
    event.value // [number, number]
    event.target // string
  }

  // 全面性检查
  type Weekday = "Mon" | "Tue" | "Wed" | "Thu" | "Fri"
  type Day = Weekday | "Sat" | "Sun"
  function getNextDay(w: Weekday): Day {
    switch (w) {
      case "Mon": return "Tue"
      // default: return "Fri" 兜底
    }
  }
}
function o() {
  type APIResponse = {
    user: {
      userId: string
      friendList: {
        count: number
        friends: {
          firstName: string
          lastName: string
        }[]
      }
    }
  }
  // 键入  不能使用点号.  只能使用方括号
  // 也可以使用GraphQL模式生成TypeScript类型
  type FriendList = APIResponse['user']['friendList']
  // number 表示 friends[]  的index索引
  // number 是 “键入”数组类型的方式。若是元组，使用0、1或其他数字字面量类型表示想“键入”的索引  
  type Friend = FriendList['friends'][number] // { firstName: string; lastName: string }


  // keyof 运算符
  // keyof运算符获取对象所有键的类型  合并为一个字符串字面量类型。
  type ResponseKeys = keyof APIResponse // 'user'
  type UserKeys = keyof APIResponse['user'] // 'userId' | 'friendList'
  type FriendListKeys = keyof APIResponse['user']['friendList'] // 'count' | 'friends'
  type Qi = {
    name: string
    age?: number
  }
  type QiKeys = keyof Qi
  let a: QiKeys = 'age'

  type Get = {
    <O extends object, K1 extends keyof O>(o: O, k: K1): O[K1]
    <O extends object, K1 extends keyof O, K2 extends keyof O[K1]>(o: O, k1: K1, k2: K2): O[K1][K2]
    <
      O extends object,
      K1 extends keyof O,
      K2 extends keyof O[K1],
      K3 extends keyof O[K1][K2]
      >
      (o: O, k1: K1, k2: K2, k3: K3): O[K1][K2][K3]
  }
  let GGet: Get = (object: any, ...keys: string[]) => {
    let res = object
    keys.forEach(k => res = res[k])
    return res
  }
  GGet({ la: 'xx', obj: { id: 1, data: { ip: 'x' } } }, 'obj', 'data', 'ip')
  function get<O extends object, K extends keyof O>(o: O, k: K): O[K] {
    return o[k]
  }
  get({ id: 'xx', name: 'xx' }, 'name')
}
function p() {
  // Record类型
  type Weekday = "Mon" | "Tue" | "Wed" | "Thu" | "Fri"
  type Day = Weekday | "Sat" | "Sun"
  let nextDay: Record<Weekday, Day> = {
    Mon: "Tue"
  }
  // 
  let nextDays: Record<Weekday, "a" | "b"> = {
    Mon: "a",
    Thu: "a",
    Wed: "b",
    Tue: "a",
    Fri: "b"
  }
  // 写这种才要extends
  type MyRecord<K extends keyof any, T> = {
    [P in K]: T;
  };
  // in就好像是循环 name不重要 Weekday被循环 就只有Key
  let nextDay1: { [Name in Weekday]: Day } = {
    Mon: 'Fri',
    Thu: "Fri",
    Wed: "Fri",
    Tue: "Fri",
    Fri: "Fri"
  }
  // 一个对象最多有一个映射类型 : 
  type MyMappedType = {
    [Key in Weekday]: Day
  }

  type Account = {
    id: number
    isEmployee: boolean
    notes: string[]
  }
  // 写这种不需要extends
  type S = {
    [Key in keyof Account]?: Account[Key] | null
  }
  // 只读
  type SS = {
    readonly [Key in keyof Account]?: Account[Key] | null
  }
  // 所有字段都是可写的 (等同于Account)  减掉readonly
  type SSS = {
    -readonly [Key in keyof SS]?: Account[Key] | null
  }
  // 所有字段都是必须的（等同于Account） 减掉 ?
  type Account1 = {
    [K in keyof Account]-?: Account[K]
  }
  // 伴生对象模式 P170
  type Currency = {
    unit: "EUP" | "GBP" | "JPY" | "USD",
    value: number
  }
  let Currency = {
    DEFAULT: "USD",
    from(value: number, unit = Currency.DEFAULT): Currency {
      return { unit, value }
    }
  }
  // import { Currency } from './Currency'

  let a = [1, true] // 想变为只读
  let aa = [1, true] as const // 如果想批量操作
  // function tuple<T extends unknown[]>(...args: T){
  //   return args
  // }
  // let ac = tuple(1, true)

  // 类型防护措施 是 typeof和instanceof细化类型的背后机制  有时需要自己的类型防护措施的能力 
  function isString(a: unknown): boolean {
    return typeof a === 'string'
  }
  function parseInput(input: string | number) {
    let formattedInput: string
    if (isString(input)) {
      // input // string | number ts细化问题
      formattedInput = input.toUpperCase()
    }
  }

  function isString1(a: unknown): a is string {
    return typeof a === 'string'
  }

  function parseInput1(input: string | number) {
    let formattedInput: string
    if (isString1(input)) {
      formattedInput = input.toUpperCase()
    }
  }
}
function q() {
  type Without<T, U> = T extends U ? never : T
  type A = Without<boolean | string | number, boolean> // string | number
  type AA = Without<boolean, boolean>
    | Without<string, boolean>
    | Without<number, boolean>
  type AAA = (boolean extends boolean ? never : boolean) |
    (string extends boolean ? never : string) |
    (number extends boolean ? never : number)
  // 所以
  type AAAA = never | string | number
  // 所以
  type A1 = string | number


  type AW = Without<boolean | string, number>

  // infer 关键字
  type ElementType<T> = T extends unknown[] ? T[number] : T
  type AE = ElementType<number[]> // number
  // 就是 T能推导U就成立 number[]肯定能推导number 所以成立
  type ElementType2<T> = T extends (infer U)[] ? U : T
  type B = ElementType2<number[]> // number


  type SecondArg<F> = F extends (a: any, b: infer B) => any ? B : never
  // 获取Array.slice类型
  // type F = typeof Array['prototype']['slice']
  type F = typeof Array.prototype.slice
  type FA = SecondArg<F> // number | undefined


  type C = Exclude<number | string, string> // number
  type MyExclude<T, U> = T extends U ? never : T

  type C1 = Extract<number | string, string> // string
  type MyExtract<T, U> = T extends U ? T : never

  type AC = { a?: number | null }
  // NonNullable<T> 从T中排除null和undefined
  type C2 = NonNullable<AC['a']> // number
  type MyNonNullable<T> = T extends undefined | null ? never : B


  // ReturnType<T> 返回函数的返回值
  type AF = (a: number) => string
  type R = ReturnType<AF> // string
  // type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any
  // 就是T能通过(...args: any) => (infer B) 推导出B  就返回B否则是 any
  type MyReturnType<T extends (...args: any) => any> = T extends (...args: any) => (infer B) ? B : any

  // (a: number, b: string) => boolean  返回 number | string  没有实现 !!!!
  type AFF = (a: string, b: number) => boolean
  type MyReturnTypeS<T extends (...args: any) => any> = T extends (...args: (infer B)) => any ? B : never
  type Test = MyReturnTypeS<AFF>

  type AP = { new(): BP }
  type BP = { b: number }
  type I = InstanceType<AP> // { b: number }
  let i: I = { b: 4 }

  // P.ts  <T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any;
  // class 的构造方法是抽象的 可能要加个抽象单词 abstract
  type MyInstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => (infer B) ? B : never

}
function r() {
  // 类型断言
  function formatInput(input: string) { }
  function getUserInput(): string | number { return 0 }
  let input = getUserInput()
  // 断定input是字符串
  formatInput(input as string)
  // 等效于
  formatInput(<string>input)

  // 非空断言 即T | null 或 T | null | undefined
  type Dialog = {
    id?: string
  }
  function closeDialog(dialog: Dialog) {
    if (!dialog.id) {
      return
    }
    setTimeout(() => {
      // let id = dialog.id
      let id = dialog.id!
    })
  }


  let userId: string
  userId.toUpperCase()

  // let userId1: string
  let userId1!: string
  fetchUser()
  userId1.toUpperCase() // ts无法通过静态方法知道userId1其实已经被赋值了  使用非空断言就ok
  function fetchUser(){
    userId1 = 'xxxxx.xxx'
  }
}
function qr(){
  type A = 'IMG' | "JPG" | "JPEG" | "JIF" | "WEBP"
  type B = {
    name: string
    age: number
    sex?: boolean
  }
  type AB<T extends keyof any> = {
    [K in T]: B
  } 
  // keyof T 能把T类似解析为一维数组 string[] 
  let a: AB<A> = {
    IMG: { name: '', age: 4 },
    JPEG: { name: '', age: 4 },
    JIF: { name: '', age: 4 },
    JPG: { name: '', age: 4 },
    WEBP: { name: '', age: 4 }
  }
  // type ABB = 
}

